/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ar.edu.unicen.twitter.suggest.backend.datasources.dao;

import ar.edu.unicen.twitter.suggest.backend.datasources.dao.util.CustomHibernateDaoSupport;
import ar.edu.unicen.twitter.suggest.backend.dtos.UsuarioDataset;
import ar.edu.unicen.twitter.suggest.backend.dtos.UsuarioDatasetAbstracto;
import ar.edu.unicen.twitter.suggest.backend.dtos.UsuarioDatasetProxy;
import ar.edu.unicen.twitter.suggest.commons.dtos.Usuario;
import java.util.ArrayList;
import java.util.Collection;

/**
 *
 * @author Alex
 */
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Query;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository("twitterDatasetDao")
public class TwitterDatasetDaoImpl extends CustomHibernateDaoSupport implements TwitterDatasetDao {

    private final static Logger LOGGER = Logger.getLogger(TwitterDatasetDaoImpl.class.getName());
    private CacheFIFO<Long, UsuarioDataset> cache_usuarioSmall;
    private static final int MAX_CACHE = 8 * 4096;

    public TwitterDatasetDaoImpl() {
        cache_usuarioSmall = new CacheFIFO<Long, UsuarioDataset>(MAX_CACHE);
    }

    @Transactional
    @Override
    public void update(UsuarioDataset u) {

        UsuarioDataset result = (UsuarioDataset) getSession().get(UsuarioDataset.class, u.getId());
        if (result == null) {
            getSession().saveOrUpdate(u);
        } else {
            getSession().merge(u);
        }
        //cache.put(u.getId(), u);
    }

    @Transactional
    @Override
    public void update(Collection<UsuarioDataset> usuarios) {

        for (UsuarioDataset usuario : usuarios) {
            update(usuario);
        }
    }

    @Transactional
    @Override
    public UsuarioDataset getUsuario(long id) {
        UsuarioDataset result = null;// cache.get(id);
//      
        if (result == null) {
//           LOGGER.log(Level.INFO, "SELECTING USER: {0} ...", id);
//            result = (UsuarioDataset) getSession().get(UsuarioDataset.class, id);
            List list = findbyHQL("from UsuarioDataset usuario where usuario.id = " + id);
            if (list.isEmpty()) {
                result = null;
            } else {
                result = (UsuarioDataset) list.get(0);
            }
            //            LOGGER.log(Level.INFO, "SELECTED USER: {0}", result);
            return result;
        }

        return result;
    }

    @Override
    @Transactional
    public UsuarioDataset getUsuarioSmall(long id) {
        //        LOGGER.log(Level.INFO, "SELECTING SMALL USER: {0} ...", id);
         UsuarioDataset result = cache_usuarioSmall.get(id);
        if (result == null) {
            List list = findbyHQL("Select new UsuarioDataset(id,followersCount,followsCount) from UsuarioDataset usuario where usuario.id = " + id);
            if (list.isEmpty()) {
                result = null;
            } else {
                result = (UsuarioDataset) list.get(0);
            }
            //       LOGGER.log(Level.INFO, "SELECTED SMALL USER: {0}", result);
            cache_usuarioSmall.put(result.getId(), result);
        }
        return result;
    }

    @Transactional
    @Override
    public void merge(UsuarioDataset usuario) {
        UsuarioDataset result = (UsuarioDataset) getSession().get(UsuarioDataset.class, usuario.getId());
        if (result == null) {
            getSession().saveOrUpdate(usuario);
        } else {
            Set<Integer> followSet = usuario.getFollowSet();
            if (followSet != null) {
                followSet.addAll(result.getFollowSet());
                usuario.setFollowSet(followSet);
            } else {
                usuario.setFollowsShortIds(result.getFollowsShortIds());
            }
            Set<Integer> followerSet = usuario.getFollowerSet();
            if (followerSet != null) {
                followerSet.addAll(result.getFollowerSet());
                usuario.setFollowerSet(followerSet);
            } else {
                usuario.setFollowersShortIds(result.getFollowsShortIds());
            }
            getSession().merge(usuario);
        }
        //cache.put(usuario.getId(), usuario);
    }

    @Transactional
    @Override
    public void merge(Collection<UsuarioDataset> usuarios) {
        for (UsuarioDataset usuario : usuarios) {
            merge(usuario);
        }
    }

    @Transactional
    @Override
    public Collection<UsuarioDataset> getUsuarios(List<Long> ids) {
//        LOGGER.log(Level.INFO, "Getting usuarios: {0}", ids);
        if (ids != null && !ids.isEmpty()) {
            Query query = getSession().createQuery("from UsuarioDataset usuario where usuario.id IN (:list)")
                    .setParameterList("list", ids);
            Collection<UsuarioDataset> resultado = query.list();
            //       LOGGER.log(Level.INFO, "Usuarios obtenidos: {0}", resultado);
            return resultado;
        } else {
            return new ArrayList<UsuarioDataset>();
        }

    }

    @Transactional
    @Override
    public Collection<UsuarioDataset> getUsuariosSmall(List<Long> ids) {

        Collection<UsuarioDataset> result = new ArrayList<UsuarioDataset>();
        if (ids != null && !ids.isEmpty()) {
            LOGGER.log(Level.INFO, "SELECTING SMALL USERs: {0} ...", ids.size());
            int PAGESIZE = 1000;
            int pageCount = (ids.size() + PAGESIZE - 1) / PAGESIZE;
            for (int page = 0; page < pageCount; ++page) {
                List list = findbyHQL("Select new UsuarioDataset(id,followersCount,followsCount) from UsuarioDataset usuario where usuario.id IN (:list)", ids.subList(page * PAGESIZE, Math.min(ids.size(), (page + 1) * PAGESIZE)));
                if (!list.isEmpty()) {
                    result.addAll(list);
                    for (Object item : list) {
                        UsuarioDataset usuario = (UsuarioDataset) item;
                        cache_usuarioSmall.put(usuario.getId(), usuario);
                    }
                }
                LOGGER.log(Level.INFO, "SELECTED SMALL USERS: PAGE {0}/{1} : {2}", new Object[]{page, pageCount, result.size()});
            }
            LOGGER.log(Level.INFO, "SELECTED SMALL USERS: {0}", result.size());
        }
        return result;
    }

    @Override
    public Usuario getUsuarioLazy(long id) {
        return new UsuarioDatasetProxy(id, this);
    }

    @Override
    public Collection<Usuario> getUsuariosLazy(List<Long> ids) {
        List<Usuario> result = new ArrayList<Usuario>();
        for (Long id : ids) {
            result.add(new UsuarioDatasetProxy(id, this));
        }
        return result;
    }

    @Override
    public int[] getSeguidores(long id) {
        int[] result;
        //        LOGGER.log(Level.INFO, "SELECTING FOLLOWERS: {0} ...", id);
//            result = (UsuarioDataset) getSession().get(UsuarioDataset.class, id);
        List list = findbyHQL("Select followersShortIds from UsuarioDataset usuario where usuario.id = " + id);
        if (list.isEmpty()) {
            result = new int[0];
        } else {
            result = (int[]) list.get(0);
        }
        //       LOGGER.log(Level.INFO, "SELECTED FOLLOWERS ({0}): {1}", new Object[]{id, result.length});
        return result;
    }

    @Override
    public int[] getSeguidos(long id) {
        int[] result;
        //        LOGGER.log(Level.INFO, "SELECTING FOLLOWS: {0} ...", id);
//            result = (UsuarioDataset) getSession().get(UsuarioDataset.class, id);
        List list = findbyHQL("Select followsShortIds from UsuarioDataset usuario where usuario.id = " + id);
        if (list.isEmpty()) {
            result = new int[0];
        } else {
            result = (int[]) list.get(0);
        }
        //        LOGGER.log(Level.INFO, "SELECTED FOLLOWS ({0}): {1}", new Object[]{id, result.length});
        return result;
    }
}